using System.Reflection;
using Microsoft.EntityFrameworkCore;
using ProduceBasicData.Api.DTO;
using ProduceBasicData.Infrastructure;

namespace ProduceBasicData.Api.Services
{
    /// <summary>
    /// 通用编码生成服务。
    /// 根据前端传入的规则，计算"前缀 + 日期片段 + 序号"，
    /// 并提供把编码写回指定实体字段的能力。
    /// </summary>
    public class CodeGeneratorService : ICodeGeneratorService
    {
        private readonly MyDBContext _db;
        private readonly Assembly _domain;

        public CodeGeneratorService(MyDBContext db)
        {
            _db = db;
            _domain = typeof(ProduceBasicData.Domain.Entities.BaseEntity).Assembly;
        }

        /// <summary>
        /// 生成编码字符串（不落库）。
        /// </summary>
        public async Task<string> GenerateAsync(GenerateCodeRequest request)
        {
            var type = _domain.GetType(request.ClassFullName, throwOnError: true)!;

            // 计算日期段
            var now = DateTime.Now;
            string datePart = request.RuleDateType switch
            {
                RuleDateType.Year => now.ToString("yyyy"),
                RuleDateType.YearMonth => now.ToString("yyyyMM"),
                RuleDateType.YearMonthDay => now.ToString("yyyyMMdd"),
                _ => string.Empty
            };

            // 查询该范围内当前最大序号
            int next = await GetNextSequenceAsync(type, request, now);

            var code = request.Prefix + (string.IsNullOrEmpty(datePart) ? string.Empty : datePart) + next.ToString().PadLeft(request.Digits, '0');
            return code;
        }

        /// <summary>
        /// 将编码写入指定记录的指定字段并保存。
        /// </summary>
        public async Task<bool> AssignAsync(AssignCodeRequest request)
        {
            var type = _domain.GetType(request.ClassFullName, throwOnError: true)!;
            var entity = await _db.FindAsync(type, request.Id);
            if (entity == null) return false;

            var prop = type.GetProperty(request.CodeFieldName, BindingFlags.Public | BindingFlags.Instance);
            if (prop == null || !prop.CanWrite) return false;

            prop.SetValue(entity, request.Code);
            await _db.SaveChangesAsync();
            return true;
        }

        /// <summary>
        /// 获取在指定范围内的下一序号（当前最大尾号 + 1）。
        /// 范围由增长类型与选择的日期字段决定。
        /// </summary>
        private async Task<int> GetNextSequenceAsync(Type type, GenerateCodeRequest request, DateTime now)
        {
            var query = GetSetQueryable(type);
            var param = System.Linq.Expressions.Expression.Parameter(type, "e");

            // 取得编码字段表达式 e => e.CodeFieldName
            var codeProp = type.GetProperty(request.CodeFieldName)!;
            var codePropExpr = System.Linq.Expressions.Expression.Property(param, codeProp);

            // 过滤条件：根据增长类型重置
            System.Linq.Expressions.Expression? predicate = null;
            if (request.GrowthType != GrowthType.Accumulate && !string.IsNullOrWhiteSpace(request.DateFieldName))
            {
                var dateProp = type.GetProperty(request.DateFieldName!);
                if (dateProp != null && (dateProp.PropertyType == typeof(DateTime) || dateProp.PropertyType == typeof(DateTime?)))
                {
                    DateTime start;
                    DateTime end;
                    switch (request.GrowthType)
                    {
                        case GrowthType.DailyReset:
                            start = now.Date; end = now.Date.AddDays(1);
                            break;
                        case GrowthType.MonthlyReset:
                            start = new DateTime(now.Year, now.Month, 1); end = start.AddMonths(1);
                            break;
                        case GrowthType.YearlyReset:
                            start = new DateTime(now.Year, 1, 1); end = start.AddYears(1);
                            break;
                        default:
                            start = DateTime.MinValue; end = DateTime.MaxValue;
                            break;
                    }

                    var datePropExpr = System.Linq.Expressions.Expression.Property(param, dateProp);
                    var startConst = System.Linq.Expressions.Expression.Constant(start, typeof(DateTime));
                    var endConst = System.Linq.Expressions.Expression.Constant(end, typeof(DateTime));

                    var ge = System.Linq.Expressions.Expression.GreaterThanOrEqual(datePropExpr, startConst);
                    var lt = System.Linq.Expressions.Expression.LessThan(datePropExpr, endConst);
                    predicate = System.Linq.Expressions.Expression.AndAlso(ge, lt);
                }
            }

            // 构造 IQueryable，并附加时间范围过滤（如有）
            if (predicate != null)
            {
                var lambda = System.Linq.Expressions.Expression.Lambda(predicate, param);
                var whereMethod = typeof(Queryable).GetMethods().First(m => m.Name == "Where" && m.GetParameters().Length == 2).MakeGenericMethod(type);
                query = (IQueryable)whereMethod.Invoke(null, new object[] { query, lambda })!;
            }

            // 前缀 + 日期部分：匹配当前范围已有编码，提取尾号
            string datePart = request.RuleDateType switch
            {
                RuleDateType.Year => now.ToString("yyyy"),
                RuleDateType.YearMonth => now.ToString("yyyyMM"),
                RuleDateType.YearMonthDay => now.ToString("yyyyMMdd"),
                _ => string.Empty
            };
            var prefix = request.Prefix + (string.IsNullOrEmpty(datePart) ? string.Empty : datePart);

            // e => e.CodeFieldName != null && e.CodeFieldName.StartsWith(prefix)
            var prefixConst = System.Linq.Expressions.Expression.Constant(prefix);
            var notNull = System.Linq.Expressions.Expression.NotEqual(codePropExpr, System.Linq.Expressions.Expression.Constant(null));
            var startsWith = System.Linq.Expressions.Expression.Call(codePropExpr, typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!, prefixConst);
            var codeFilter = System.Linq.Expressions.Expression.AndAlso(notNull, startsWith);
            var codeLambda = System.Linq.Expressions.Expression.Lambda(codeFilter, param);
            var where2 = typeof(Queryable).GetMethods().First(m => m.Name == "Where" && m.GetParameters().Length == 2).MakeGenericMethod(type);
            query = (IQueryable)where2.Invoke(null, new object[] { query, codeLambda })!;

            // 取最大尾号
            var selectLambda = System.Linq.Expressions.Expression.Lambda(codePropExpr, param);
            var selectMethod = typeof(Queryable).GetMethods().First(m => m.Name == "Select" && m.GetParameters().Length == 2).MakeGenericMethod(type, typeof(string));
            var codesQuery = (IQueryable<string>)selectMethod.Invoke(null, new object[] { query, selectLambda })!;

            var maxTail = 0;
            await foreach (var code in codesQuery.AsAsyncEnumerable())
            {
                if (code != null && code.StartsWith(prefix))
                {
                    var tail = code.Substring(prefix.Length);
                    if (int.TryParse(tail, out var n))
                    {
                        if (n > maxTail) maxTail = n;
                    }
                }
            }

            return maxTail + 1;
        }

        /// <summary>
        /// 通过反射调用 DbContext.Set<TEntity>() 获取对应实体的 IQueryable。
        /// </summary>
        private IQueryable GetSetQueryable(Type entityType)
        {
            var method = typeof(DbContext).GetMethods()
                .First(m => m.Name == "Set" && m.IsGenericMethod && m.GetParameters().Length == 0);
            var generic = method.MakeGenericMethod(entityType);
            var dbSetObj = generic.Invoke(_db, null)!;
            return (IQueryable)dbSetObj;
        }
    }
}


